package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.factory.StrategyFactory;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.strategy.ItemTypeStrategy;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.LocalDateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private StrategyFactory strategyFactory;

    /**
     * 微信授权登录
     * 该方法通过微信提供的code获取会话信息，并处理用户信息的保存和登录状态的生成
     *
     * @param code 微信登录时获取的code，用于换取会话密钥
     * @return 返回包含登录token的Map对象
     */
    @Override
    public Map<String, Object> wxLogin(String code) {
        // 通过微信code获取会话信息，包括openid等
        WxMaJscode2SessionResult wxMaJscode2SessionResult = null;
        try {
            wxMaJscode2SessionResult = wxMaService.jsCode2SessionInfo(code);
        } catch (WxErrorException e) {
            // 如果微信服务器返回错误，抛出运行时异常
            throw new RuntimeException(e);
        }

        // 获取openid
        String openId = wxMaJscode2SessionResult.getOpenid();

        // 根据openid查询用户信息，如果不存在则创建新用户
        UserInfo userInfo = this.getOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getWxOpenId, openId));

        if (userInfo == null) {
            userInfo = new UserInfo();
            userInfo.setWxOpenId(openId);
            // 设置默认昵称和头像，并保存用户信息
            userInfo.setNickname("听友：" + System.currentTimeMillis());
            userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
            // 保存用户信息
            this.save(userInfo);

            // 用户注册后，发送消息到消息队列，通知其他服务
            rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, userInfo.getId());
        }

        // 生成登录token，并将其与用户信息关联保存到Redis中
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        this.redisTemplate.opsForValue().set(userLoginKey, userInfo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);

        // 将token放入返回的Map对象中
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        // 返回Map对象
        return map;
    }

    /**
     * 根据用户ID获取用户信息
     * 该方法用于封装用户信息的获取过程，通过用户ID查询数据库，并将结果转换为UserInfoVo对象返回
     *
     * @param userId 用户ID，用于查询特定用户信息
     * @return UserInfoVo 返回用户信息的视图对象，包含用户的基本信息
     */
    @Override
    public UserInfoVo getUserInfoById(Long userId) {
        // 初始化用户信息视图对象
        UserInfoVo userInfoVo = new UserInfoVo();
        // 通过用户ID从数据库中获取用户信息
        UserInfo userInfo = this.getById(userId);
        // 将UserInfo对象的属性复制到UserInfoVo对象中，实现对象间的属性转换
        BeanUtils.copyProperties(userInfo, userInfoVo);
        // 返回填充好的用户信息视图对象
        return userInfoVo;
    }

    /**
     * 更新用户信息
     * <p>
     * 该方法接收用户信息的更新数据和用户ID，将更新数据应用到对应的用户记录上
     * 主要用于在用户信息发生变化时，根据用户ID更新数据库中的用户记录
     *
     * @param userInfoVo 包含用户更新信息的对象，包括昵称和头像URL等信息
     * @param userId     需要更新的用户ID
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo, Long userId) {
        // 创建一个UserInfo对象，用于将更新信息应用到数据库
        UserInfo userInfo = new UserInfo();
        // 设置用户ID，指定需要更新的用户记录
        userInfo.setId(userId);
        // 设置用户的昵称，根据userInfoVo中的数据进行更新
        userInfo.setNickname(userInfoVo.getNickname());
        // 设置用户的头像URL，根据userInfoVo中的数据进行更新
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        // 调用userInfoMapper的updateById方法，根据用户ID更新数据库中的用户记录
        this.userInfoMapper.updateById(userInfo);
    }

    /**
     * 判断用户是否已购买专辑下的声音
     * 该方法用于判断用户是否已购买专辑，返回一个Map对象，键为专辑ID，值为购买状态（0表示未购买，1表示已购买）
     *
     * @param albumId     专辑ID
     * @param trackIdList 声音ID列表
     * @param userId      用户ID
     * @return 包含专辑购买状态的Map对象
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long albumId, List<Long> trackIdList, Long userId) {
        // 检查用户是否已购买整个专辑
        UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(
                new LambdaQueryWrapper<UserPaidAlbum>()
                        .eq(UserPaidAlbum::getAlbumId, albumId)
                        .eq(UserPaidAlbum::getUserId, userId)
        );
        // 如果用户已购买专辑，则将所有歌曲标记为已购买
        if (null != userPaidAlbum) {
            return trackIdList.stream().collect(Collectors.toMap(trackId -> trackId, trackId -> 1));
        }
        // 如果用户未购买专辑，则检查用户是否已购买列表中的歌曲
        LambdaQueryWrapper<UserPaidTrack> wrapper = new LambdaQueryWrapper<UserPaidTrack>()
                .eq(UserPaidTrack::getAlbumId, albumId)
                .eq(UserPaidTrack::getUserId, userId)
                .in(UserPaidTrack::getTrackId, trackIdList);
        List<Long> trackIdPaidList = userPaidTrackMapper.selectList(wrapper).stream()
                .map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        // 创建一个HashMap来存储歌曲的购买状态
        HashMap<Long, Integer> map = new HashMap<>();
        // 遍历歌曲ID列表，根据用户是否已购买来设置购买状态
        for (Long trackId : trackIdList) {
            if (trackIdPaidList.contains(trackId)) {
                map.put(trackId, 1);
            } else {
                map.put(trackId, 0);
            }
        }
        // 返回包含歌曲购买状态的Map对象
        return map;
    }

    @Override
    public Boolean isPaidAlbum(Long albumId, Long userId) {
        // albumId userId 在表中有且只有一条记录
        // 检查用户是否已购买整个专辑
        return userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getAlbumId, albumId).eq(UserPaidAlbum::getUserId, userId)) != null;
    }

    /**
     * 根据专辑id获取用户支付过的声音id列表
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public List<Long> findUserTrackPaidList(Long userId, Long albumId) {
        // 根据用户Id与专辑Id获取已购声音集合
        List<Long> trackIdList = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userId).eq(UserPaidTrack::getAlbumId, albumId)).stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        // 返回数据
        return trackIdList;
    }

    /**
     * 处理用户购买记录
     *
     * @param userPaidRecordVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUserPaidRecord(UserPaidRecordVo userPaidRecordVo) {
        // 根据itemType获取对应的策略对象
        ItemTypeStrategy strategy = strategyFactory.writePaidRecode(userPaidRecordVo.getItemType());
        strategy.savePaidRecord(userPaidRecordVo);

//        // 本质是保存数据
//        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(userPaidRecordVo.getItemType())) {
//            // 先查询是否有购买记录
//            Boolean result = isPaidAlbum(userPaidRecordVo.getItemIdList().get(0), userPaidRecordVo.getUserId());
//            if (result) return;
//            // 添加购买专辑信息
//            UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
//            userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
//            userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
//            userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
//            userPaidAlbumMapper.insert(userPaidAlbum);
//        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(userPaidRecordVo.getItemType())) {
//            // 添加购买声音 ：如果只有小程序端(单线程)，不会出现脏数据。但是如果是多平台(pc，h5端)，会出现脏数据
//            // for (Long trackId : userPaidRecordVo.getItemIdList()) {
//            // 通过用户Id，专辑Id 查询到已购买的集合; 判断已购买的集合中是否包含trackId; 如果包含，不能执行插入。使用continue; 可能需要退还对应的金额;
//            // 否则，直接执行插入数据；
//            // }
//            Long count = this.userPaidTrackMapper.selectCount(
//                    new LambdaQueryWrapper<UserPaidTrack>()
//                            .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
//                            .eq(UserPaidTrack::getUserId, userPaidRecordVo.getUserId()));
//            if (count > 0) return;
//            // 获取当前订单明细
//            List<Long> trackIdList = userPaidRecordVo.getItemIdList();
//            // 可以通过声音Id 查询声音对象
//            Result<TrackInfo> trackInfoResult = trackInfoFeignClient.getTrackInfoById(trackIdList.get(0));
//            Assert.notNull(trackInfoResult, "查询购买声音结果集为空");
//            TrackInfo trackInfo = trackInfoResult.getData();
//            Assert.notNull(trackInfo, "查询购买声音对象为空");
//            for (Long trackId : trackIdList) {
//                // 插入数据
//                UserPaidTrack userPaidTrack = new UserPaidTrack();
//                userPaidTrack.setUserId(userPaidRecordVo.getUserId());
//                userPaidTrack.setAlbumId(trackInfo.getAlbumId());
//                userPaidTrack.setTrackId(trackId);
//                userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
//                userPaidTrackMapper.insert(userPaidTrack);
//            }
//        } else {
//            // 添加购买VIP，续期
//            // 先获取购买VIP的服务配置信息 1，3，12
//            Long vipServiceConfigId = userPaidRecordVo.getItemIdList().get(0);
//            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(vipServiceConfigId);
//            // 获取购买的月数
//            Integer serviceMonth = vipServiceConfig.getServiceMonth();
//            // 判断：是否属于续期情况（当前用户属于vip且没有过期）
//            UserInfo userInfo = this.userInfoMapper.selectById(userPaidRecordVo.getUserId());
//            // 创建购买VIP记录对象
//            UserVipService userVipService = new UserVipService();
//            userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
//            userVipService.setUserId(userPaidRecordVo.getUserId());
//            // 获取当前系统事件
//            Date currentTime = new Date();
//            // 没有续期的情况
//            userVipService.setStartTime(currentTime);
//            // 续期：start_time 开始时间; expire_time 过期时间 在原有的过期时间基础上，再加上购买的时长
//            if (1 == userInfo.getIsVip() && userInfo.getVipExpireTime().after(new Date())) {
//                currentTime = userInfo.getVipExpireTime();
//            }
//            // 当前系统时间+购买时间
//            Date expireTime = new LocalDateTime(currentTime).plusMonths(serviceMonth).toDate();
//            userVipService.setExpireTime(expireTime);
//            // 保存当前用户购买记录
//            userVipServiceMapper.insert(userVipService);
//            // 判断是否属于VIP
//            userInfo.setIsVip(1);
//            userInfo.setVipExpireTime(expireTime);
//            // 更新数据
//            this.userInfoMapper.updateById(userInfo);
//        }
//    }

    }

}

